#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os

from dws.commons.common import check_sh_cmd_param, valid_backup_id
from common.common import exter_attack
from common.util.check_utils import is_valid_uuid
from common.const import RepositoryDataTypeEnum
from dws.commons.const import JsonKey, CopyGenerateType, DwsSubType
from dws.commons.function_tool import log_start


class DwsParamParse:

    @staticmethod
    @log_start()
    def check_path(path_param):
        if not os.path.realpath(path_param).startswith('/mnt/databackup/'):
            raise Exception(f'Param of path:{path_param} is illegal.')
        return True

    @staticmethod
    @exter_attack
    def get_data_reps(param):
        data_reps = []
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.DATA_REPOSITORY:
                if len(rep.get("path", [])) > 0 and DwsParamParse.check_path(rep.get("path")[0]):
                    data_reps.append(rep)

        if len(data_reps) == 0:
            raise Exception('Param of data_reps err')

        return data_reps

    @staticmethod
    @exter_attack
    def get_meta_rep(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                if len(rep.get("path", [])) > 0 and DwsParamParse.check_path(rep.get("path")[0]):
                    return rep
        raise Exception('Param of meta_rep err')

    @staticmethod
    @exter_attack
    def get_cache_rep(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.CACHE_REPOSITORY:
                if len(rep.get("path", [])) > 0 and DwsParamParse.check_path(rep.get("path")[0]):
                    return rep
        raise Exception('Param of cache_rep err')

    @staticmethod
    @exter_attack
    def get_cache_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.CACHE_REPOSITORY:
                if len(rep.get("path", [])) > 0 and DwsParamParse.check_path(rep.get("path")[0]):
                    return rep.get("path")[0]
        raise Exception('Param of cache_path err')

    @staticmethod
    @exter_attack
    def get_meta_path(param):
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("repositoryType") == RepositoryDataTypeEnum.META_REPOSITORY:
                if len(rep.get("path", [])) > 0 and DwsParamParse.check_path(rep.get("path")[0]):
                    return rep.get("path")[0]
        raise Exception('Param of meta_path err')

    @staticmethod
    @exter_attack
    def get_user_name(param):
        user_name = param.get("job", {}).get("protectEnv", {}).get("extendInfo", {}).get("DwsUser")
        if not check_sh_cmd_param(user_name):
            raise Exception("Param of user_name invalid")
        if user_name.find(" ") != -1:
            raise Exception("Param of user_name invalid")
        return user_name

    @staticmethod
    @exter_attack
    def get_env_path(param):
        env_path = param.get("job", {}).get("protectEnv", {}).get("extendInfo", {}).get("envFile")
        if not check_sh_cmd_param(env_path):
            raise Exception("Param of env_path invalid")
        return env_path

    @staticmethod
    @exter_attack
    def get_copy_id(param):
        copy_list = param.get("job", {}).get("copy")
        if len(copy_list) != 0:
            if is_valid_uuid(copy_list[0].get("id", None)):
                return copy_list[0].get("id", None)
        raise Exception('Param of copy_id err')

    @staticmethod
    @exter_attack
    def get_type(param):
        return param.get("job", {}).get("jobParam", {}).get("backupType")

    @staticmethod
    @exter_attack
    def get_job_id(param):
        job_id = param.get("job", {}).get("jobId")
        if not check_sh_cmd_param(job_id):
            raise Exception("Param of job_id invalid")
        return job_id

    @staticmethod
    @exter_attack
    def get_sub_job_id(param):
        sub_job_id = param.get("subJob", {}).get("subJobId")
        if not sub_job_id:
            return sub_job_id
        if not check_sh_cmd_param(sub_job_id):
            raise Exception("Param of sub_job_id invalid")
        return sub_job_id

    @staticmethod
    @exter_attack
    def get_backup_result(param):
        return param.get("backupJobResult")

    @staticmethod
    @exter_attack
    def get_sub_job_name(param):
        return param.get("subJob", {}).get("jobName", "")

    @staticmethod
    @exter_attack
    def get_metadata_destination(param):
        metadata_des_path = os.path.realpath(param.get("job", {}).get("extendInfo", {}).get("metadataPath"))
        if not check_sh_cmd_param(metadata_des_path):
            raise Exception("Param of metadata_des_path invalid")
        return metadata_des_path

    @staticmethod
    @exter_attack
    def get_nodes(param):
        return param.get("job", {}).get("protectEnv", {}).get("nodes")

    @staticmethod
    @exter_attack
    def get_storage_id(param):
        storage_id = param.get("job", {}).get("extendInfo", {}).get("storage_id")
        return storage_id

    @staticmethod
    @exter_attack
    def get_storage_type(param):
        return param.get("job", {}).get("extendInfo", {}).get("storage_type")

    @staticmethod
    @exter_attack
    def get_protect_env(param):
        return param.get("job", {}).get("protectEnv")

    @staticmethod
    @exter_attack
    def get_backup_tool_type(param):
        subtype = param.get("job", {}).get("protectObject", {}).get("subType", "")
        if not subtype:
            return CopyGenerateType.INVALID_GENERATE_TYPE
        if subtype == DwsSubType.DWS_TABLE:
            return CopyGenerateType.GENERATE_BY_GDS
        return int(param.get("job", {}).get("extendInfo", {}).get("backupToolType", "0"))

    @staticmethod
    @exter_attack
    def get_res_name(param):
        res_name = param.get("job", {}).get("protectObject", {}).get("name")
        if not check_sh_cmd_param(res_name):
            raise Exception("Param of res_name invalid")
        return res_name

    @staticmethod
    @exter_attack
    def get_protect_obj_extend_info(param):
        return param.get("job", {}).get("protectObject", {}).get("extendInfo", {})

    @staticmethod
    @exter_attack
    def get_protect_obj(param):
        return param.get("job", {}).get("protectObject", {})

    @staticmethod
    @exter_attack
    def get_esn(param):
        esn_list = set()
        for rep in param.get("job", {}).get("repositories"):
            if rep.get("id"):
                esn_list.add(rep.get("id"))
        return esn_list

    @staticmethod
    @exter_attack
    def get_agents(param):
        agents = set()
        for item in param.get("job", {}).get("extendInfo", {}).get("agents", []):
            agents.add(item.get("id", ""))

        return list(agents)

    @staticmethod
    @exter_attack
    def get_failed_agents(param):
        failed_agents = set()
        for item in param.get("job", {}).get("extendInfo", {}).get("failedAgents", []):
            failed_agents.add(item.get("id", ""))

        return list(failed_agents)

    @staticmethod
    @exter_attack
    def get_copies(param):
        return param.get("job", {}).get("copies", [])

    @staticmethod
    @exter_attack
    def get_cluster_agents(param):
        ip_str = param.get("job", {}).get("protectEnv", {}).get("extendInfo", {}).get("clusterAgent", "")
        ret_ip_list = []
        for host_ips in ip_str.split(";"):
            tmp_ip = host_ips.split(",")[0]
            if tmp_ip:
                ret_ip_list.append(tmp_ip)

        return ret_ip_list

    @staticmethod
    @exter_attack
    def get_host_agents(param):
        ip_str = param.get("job", {}).get("protectEnv", {}).get("extendInfo", {}).get("hostAgent", "")
        ret_ip_list = []
        for host_ips in ip_str.split(";"):
            tmp_ip = host_ips.strip().split(",")[0]
            if tmp_ip:
                ret_ip_list.append(tmp_ip)

        return ret_ip_list

    @staticmethod
    @exter_attack
    @log_start()
    def is_open_source_delete(param):
        return param.get("job", {}).get("jobParam", {}).get("dataLayout", {}).get("extendInfo", {}).get("srcDeduption")

    @staticmethod
    @exter_attack
    def is_physical_fine_grained(param):
        """
        解析是否使用细粒度参数
        :param param: 任务参数
        :return: 使用细粒度参数返回True，不使用返回False
        :raise: 解析失败抛出异常
        """
        subtype = param.get("job", {}).get("protectObject", {}).get("subType", "")
        if not subtype:
            raise Exception("Param of subType is None")
        if subtype != DwsSubType.DWS_CLUSTER:
            return True
        flag = param.get("job", {}).get("extendInfo", {}).get("physical_fine_grained")
        if flag is None:
            raise Exception("Param of physical_fine_grained is None")
        if not isinstance(flag, str):
            raise Exception("Param of physical_fine_grained should be a string")
        if flag.lower() == 'true':
            return True
        if flag.lower() == 'false':
            return False
        raise Exception("Param of physical_fine_grained invalid")


class CopyInfoParam:

    @staticmethod
    @exter_attack
    def get_storage_id(param):
        storage_id = param.get('extendInfo', {}).get(JsonKey.STORAGE_ID, None)
        return storage_id

    @staticmethod
    @exter_attack
    def get_version(param):
        return param.get('extendInfo', {}).get(JsonKey.VERSION, None)

    @staticmethod
    @exter_attack
    def get_host_ip(param):
        return param.get('extendInfo', {}).get(JsonKey.HOST_IP, None)

    @staticmethod
    @exter_attack
    def get_cluster_nodes(param):
        return param.get('extendInfo', {}).get(JsonKey.CLUSTER_NODES, None)

    @staticmethod
    @exter_attack
    def get_storage_esn(param):
        return param.get('extendInfo', {}).get(JsonKey.STORAGE_ESN, None)

    @staticmethod
    @exter_attack
    def get_backup_key(param):
        backup_key = param.get('extendInfo', {}).get(JsonKey.BACKUP_KEY, None)
        # 表集副本删除传的backup_key是写死的gds
        if (backup_key == "gds"):
            return backup_key
        if not valid_backup_id(backup_key):
            raise Exception("Param of backup_key invalid")
        return backup_key

    @staticmethod
    @exter_attack
    def get_copy_id(param):
        copy_id = param.get("id")
        if not is_valid_uuid(copy_id):
            raise Exception("Param of copy_id invalid")
        return copy_id

    @staticmethod
    @exter_attack
    def get_agent_id(param):
        agent_id = param.get('extendInfo', {}).get(JsonKey.AGENT_ID, None)
        return agent_id

    @staticmethod
    @exter_attack
    def get_metadata_destination(param):
        metadata_des_path = os.path.realpath(param.get('extendInfo', {}).get(JsonKey.METADATA_PATH, None))
        if not check_sh_cmd_param(metadata_des_path):
            raise Exception("Param of metadata_des_path invalid")
        return metadata_des_path

    @staticmethod
    @log_start()
    def is_normal(param):
        """
        判断参数是否变化， if not 不需要转全量
        """
        if not CopyInfoParam.get_version(param) or \
                not CopyInfoParam.get_host_ip(param):
            return False
        if not CopyInfoParam.get_backup_key(param) or \
                not CopyInfoParam.get_metadata_destination(param):
            return False
        return True
